/*
 * jQuery Mobile v1.4.5
 * http://jquerymobile.com
 *
 * Copyright 2010, 2014 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 */

(function (root, doc, factory) {
    if (typeof define === "function" && define.amd) {
        // AMD. Register as an anonymous module.
        define(["jquery"], function ($) {
            factory($, root, doc);
            return $.mobile;
        });
    } else {
        // Browser globals
        factory(root.jQuery, root, doc);
    }
}(this, document, function (jQuery, window, document, undefined) {// This plugin is an experiment for abstracting away the touch and mouse
// events so that developers don't have to worry about which method of input
// the device their document is loaded on supports.
//
// The idea here is to allow the developer to register listeners for the
// basic mouse events, such as mousedown, mousemove, mouseup, and click,
// and the plugin will take care of registering the correct listeners
// behind the scenes to invoke the listener at the fastest possible time
// for that device, while still retaining the order of event firing in
// the traditional mouse environment, should multiple handlers be registered
// on the same element for different events.
//
// The current version exposes the following virtual events to jQuery bind methods:
// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"

    (function ($, window, document, undefined) {

        var dataPropertyName = "virtualMouseBindings",
            touchTargetPropertyName = "virtualTouchID",
            virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),
            touchEventProps = "clientX clientY pageX pageY screenX screenY".split(" "),
            mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
            mouseEventProps = $.event.props.concat(mouseHookProps),
            activeDocHandlers = {},
            resetTimerID = 0,
            startX = 0,
            startY = 0,
            didScroll = false,
            clickBlockList = [],
            blockMouseTriggers = false,
            blockTouchTriggers = false,
            eventCaptureSupported = "addEventListener" in document,
            $document = $(document),
            nextTouchID = 1,
            lastTouchID = 0, threshold,
            i;

        $.vmouse = {
            moveDistanceThreshold: 10,
            clickDistanceThreshold: 10,
            resetTimerDuration: 1500
        };

        function getNativeEvent(event) {

            while (event && typeof event.originalEvent !== "undefined") {
                event = event.originalEvent;
            }
            return event;
        }

        function createVirtualEvent(event, eventType) {

            var t = event.type,
                oe, props, ne, prop, ct, touch, i, j, len;

            event = $.Event(event);
            event.type = eventType;

            oe = event.originalEvent;
            props = $.event.props;

            // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
            // https://github.com/jquery/jquery-mobile/issues/3280
            if (t.search(/^(mouse|click)/) > -1) {
                props = mouseEventProps;
            }

            // copy original event properties over to the new event
            // this would happen if we could call $.event.fix instead of $.Event
            // but we don't have a way to force an event to be fixed multiple times
            if (oe) {
                for (i = props.length, prop; i;) {
                    prop = props[--i];
                    event[prop] = oe[prop];
                }
            }

            // make sure that if the mouse and click virtual events are generated
            // without a .which one is defined
            if (t.search(/mouse(down|up)|click/) > -1 && !event.which) {
                event.which = 1;
            }

            if (t.search(/^touch/) !== -1) {
                ne = getNativeEvent(oe);
                t = ne.touches;
                ct = ne.changedTouches;
                touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[0] : undefined );

                if (touch) {
                    for (j = 0, len = touchEventProps.length; j < len; j++) {
                        prop = touchEventProps[j];
                        event[prop] = touch[prop];
                    }
                }
            }

            return event;
        }

        function getVirtualBindingFlags(element) {

            var flags = {},
                b, k;

            while (element) {

                b = $.data(element, dataPropertyName);

                for (k in b) {
                    if (b[k]) {
                        flags[k] = flags.hasVirtualBinding = true;
                    }
                }
                element = element.parentNode;
            }
            return flags;
        }

        function getClosestElementWithVirtualBinding(element, eventType) {
            var b;
            while (element) {

                b = $.data(element, dataPropertyName);

                if (b && ( !eventType || b[eventType] )) {
                    return element;
                }
                element = element.parentNode;
            }
            return null;
        }

        function enableTouchBindings() {
            blockTouchTriggers = false;
        }

        function disableTouchBindings() {
            blockTouchTriggers = true;
        }

        function enableMouseBindings() {
            lastTouchID = 0;
            clickBlockList.length = 0;
            blockMouseTriggers = false;

            // When mouse bindings are enabled, our
            // touch bindings are disabled.
            disableTouchBindings();
        }

        function disableMouseBindings() {
            // When mouse bindings are disabled, our
            // touch bindings are enabled.
            enableTouchBindings();
        }

        function startResetTimer() {
            clearResetTimer();
            resetTimerID = setTimeout(function () {
                resetTimerID = 0;
                enableMouseBindings();
            }, $.vmouse.resetTimerDuration);
        }

        function clearResetTimer() {
            if (resetTimerID) {
                clearTimeout(resetTimerID);
                resetTimerID = 0;
            }
        }

        function triggerVirtualEvent(eventType, event, flags) {
            var ve;

            if (( flags && flags[eventType] ) ||
                ( !flags && getClosestElementWithVirtualBinding(event.target, eventType) )) {

                ve = createVirtualEvent(event, eventType);

                $(event.target).trigger(ve);
            }

            return ve;
        }

        function mouseEventCallback(event) {
            var touchID = $.data(event.target, touchTargetPropertyName),
                ve;

            if (!blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID )) {
                ve = triggerVirtualEvent("v" + event.type, event);
                if (ve) {
                    if (ve.isDefaultPrevented()) {
                        event.preventDefault();
                    }
                    if (ve.isPropagationStopped()) {
                        event.stopPropagation();
                    }
                    if (ve.isImmediatePropagationStopped()) {
                        event.stopImmediatePropagation();
                    }
                }
            }
        }

        function handleTouchStart(event) {

            var touches = getNativeEvent(event).touches,
                target, flags, t;

            if (touches && touches.length === 1) {

                target = event.target;
                flags = getVirtualBindingFlags(target);

                if (flags.hasVirtualBinding) {

                    lastTouchID = nextTouchID++;
                    $.data(target, touchTargetPropertyName, lastTouchID);

                    clearResetTimer();

                    disableMouseBindings();
                    didScroll = false;

                    t = getNativeEvent(event).touches[0];
                    startX = t.pageX;
                    startY = t.pageY;

                    triggerVirtualEvent("vmouseover", event, flags);
                    triggerVirtualEvent("vmousedown", event, flags);
                }
            }
        }

        function handleScroll(event) {
            if (blockTouchTriggers) {
                return;
            }

            if (!didScroll) {
                triggerVirtualEvent("vmousecancel", event, getVirtualBindingFlags(event.target));
            }

            didScroll = true;
            startResetTimer();
        }

        function handleTouchMove(event) {
            if (blockTouchTriggers) {
                return;
            }

            var t = getNativeEvent(event).touches[0],
                didCancel = didScroll,
                moveThreshold = $.vmouse.moveDistanceThreshold,
                flags = getVirtualBindingFlags(event.target);

            didScroll = didScroll ||
                ( Math.abs(t.pageX - startX) > moveThreshold ||
                Math.abs(t.pageY - startY) > moveThreshold );

            if (didScroll && !didCancel) {
                triggerVirtualEvent("vmousecancel", event, flags);
            }

            triggerVirtualEvent("vmousemove", event, flags);
            startResetTimer();
        }

        function handleTouchEnd(event) {
            if (blockTouchTriggers) {
                return;
            }

            disableTouchBindings();

            var flags = getVirtualBindingFlags(event.target),
                ve, t;
            triggerVirtualEvent("vmouseup", event, flags);

            if (!didScroll) {
                ve = triggerVirtualEvent("vclick", event, flags);
                if (ve && ve.isDefaultPrevented()) {
                    // The target of the mouse events that follow the touchend
                    // event don't necessarily match the target used during the
                    // touch. This means we need to rely on coordinates for blocking
                    // any click that is generated.
                    t = getNativeEvent(event).changedTouches[0];
                    clickBlockList.push({
                        touchID: lastTouchID,
                        x: t.clientX,
                        y: t.clientY
                    });

                    // Prevent any mouse events that follow from triggering
                    // virtual event notifications.
                    blockMouseTriggers = true;
                }
            }
            triggerVirtualEvent("vmouseout", event, flags);
            didScroll = false;

            startResetTimer();
        }

        function hasVirtualBindings(ele) {
            var bindings = $.data(ele, dataPropertyName),
                k;

            if (bindings) {
                for (k in bindings) {
                    if (bindings[k]) {
                        return true;
                    }
                }
            }
            return false;
        }

        function dummyMouseHandler() {
        }

        function getSpecialEventObject(eventType) {
            var realType = eventType.substr(1);

            return {
                setup: function (/* data, namespace */) {
                    // If this is the first virtual mouse binding for this element,
                    // add a bindings object to its data.

                    if (!hasVirtualBindings(this)) {
                        $.data(this, dataPropertyName, {});
                    }

                    // If setup is called, we know it is the first binding for this
                    // eventType, so initialize the count for the eventType to zero.
                    var bindings = $.data(this, dataPropertyName);
                    bindings[eventType] = true;

                    // If this is the first virtual mouse event for this type,
                    // register a global handler on the document.

                    activeDocHandlers[eventType] = ( activeDocHandlers[eventType] || 0 ) + 1;

                    if (activeDocHandlers[eventType] === 1) {
                        $document.bind(realType, mouseEventCallback);
                    }

                    // Some browsers, like Opera Mini, won't dispatch mouse/click events
                    // for elements unless they actually have handlers registered on them.
                    // To get around this, we register dummy handlers on the elements.

                    $(this).bind(realType, dummyMouseHandler);

                    // For now, if event capture is not supported, we rely on mouse handlers.
                    if (eventCaptureSupported) {
                        // If this is the first virtual mouse binding for the document,
                        // register our touchstart handler on the document.

                        activeDocHandlers["touchstart"] = ( activeDocHandlers["touchstart"] || 0) + 1;

                        if (activeDocHandlers["touchstart"] === 1) {
                            $document.bind("touchstart", handleTouchStart)
                                .bind("touchend", handleTouchEnd)

                                // On touch platforms, touching the screen and then dragging your finger
                                // causes the window content to scroll after some distance threshold is
                                // exceeded. On these platforms, a scroll prevents a click event from being
                                // dispatched, and on some platforms, even the touchend is suppressed. To
                                // mimic the suppression of the click event, we need to watch for a scroll
                                // event. Unfortunately, some platforms like iOS don't dispatch scroll
                                // events until *AFTER* the user lifts their finger (touchend). This means
                                // we need to watch both scroll and touchmove events to figure out whether
                                // or not a scroll happenens before the touchend event is fired.

                                .bind("touchmove", handleTouchMove)
                                .bind("scroll", handleScroll);
                        }
                    }
                },

                teardown: function (/* data, namespace */) {
                    // If this is the last virtual binding for this eventType,
                    // remove its global handler from the document.

                    --activeDocHandlers[eventType];

                    if (!activeDocHandlers[eventType]) {
                        $document.unbind(realType, mouseEventCallback);
                    }

                    if (eventCaptureSupported) {
                        // If this is the last virtual mouse binding in existence,
                        // remove our document touchstart listener.

                        --activeDocHandlers["touchstart"];

                        if (!activeDocHandlers["touchstart"]) {
                            $document.unbind("touchstart", handleTouchStart)
                                .unbind("touchmove", handleTouchMove)
                                .unbind("touchend", handleTouchEnd)
                                .unbind("scroll", handleScroll);
                        }
                    }

                    var $this = $(this),
                        bindings = $.data(this, dataPropertyName);

                    // teardown may be called when an element was
                    // removed from the DOM. If this is the case,
                    // jQuery core may have already stripped the element
                    // of any data bindings so we need to check it before
                    // using it.
                    if (bindings) {
                        bindings[eventType] = false;
                    }

                    // Unregister the dummy event handler.

                    $this.unbind(realType, dummyMouseHandler);

                    // If this is the last virtual mouse binding on the
                    // element, remove the binding data from the element.

                    if (!hasVirtualBindings(this)) {
                        $this.removeData(dataPropertyName);
                    }
                }
            };
        }

// Expose our custom events to the jQuery bind/unbind mechanism.

        for (i = 0; i < virtualEventNames.length; i++) {
            $.event.special[virtualEventNames[i]] = getSpecialEventObject(virtualEventNames[i]);
        }

// Add a capture click handler to block clicks.
// Note that we require event capture support for this so if the device
// doesn't support it, we punt for now and rely solely on mouse events.
        if (eventCaptureSupported) {
            document.addEventListener("click", function (e) {
                var cnt = clickBlockList.length,
                    target = e.target,
                    x, y, ele, i, o, touchID;

                if (cnt) {
                    x = e.clientX;
                    y = e.clientY;
                    threshold = $.vmouse.clickDistanceThreshold;

                    // The idea here is to run through the clickBlockList to see if
                    // the current click event is in the proximity of one of our
                    // vclick events that had preventDefault() called on it. If we find
                    // one, then we block the click.
                    //
                    // Why do we have to rely on proximity?
                    //
                    // Because the target of the touch event that triggered the vclick
                    // can be different from the target of the click event synthesized
                    // by the browser. The target of a mouse/click event that is synthesized
                    // from a touch event seems to be implementation specific. For example,
                    // some browsers will fire mouse/click events for a link that is near
                    // a touch event, even though the target of the touchstart/touchend event
                    // says the user touched outside the link. Also, it seems that with most
                    // browsers, the target of the mouse/click event is not calculated until the
                    // time it is dispatched, so if you replace an element that you touched
                    // with another element, the target of the mouse/click will be the new
                    // element underneath that point.
                    //
                    // Aside from proximity, we also check to see if the target and any
                    // of its ancestors were the ones that blocked a click. This is necessary
                    // because of the strange mouse/click target calculation done in the
                    // Android 2.1 browser, where if you click on an element, and there is a
                    // mouse/click handler on one of its ancestors, the target will be the
                    // innermost child of the touched element, even if that child is no where
                    // near the point of touch.

                    ele = target;

                    while (ele) {
                        for (i = 0; i < cnt; i++) {
                            o = clickBlockList[i];
                            touchID = 0;

                            if (( ele === target && Math.abs(o.x - x) < threshold && Math.abs(o.y - y) < threshold ) ||
                                $.data(ele, touchTargetPropertyName) === o.touchID) {
                                // XXX: We may want to consider removing matches from the block list
                                //      instead of waiting for the reset timer to fire.
                                e.preventDefault();
                                e.stopPropagation();
                                return;
                            }
                        }
                        ele = ele.parentNode;
                    }
                }
            }, true);
        }
    })(jQuery, window, document);

    (function ($) {
        $.mobile = {};
    }(jQuery));

    (function ($, undefined) {
        var support = {
            touch: "ontouchend" in document
        };

        $.mobile.support = $.mobile.support || {};
        $.extend($.support, support);
        $.extend($.mobile.support, support);
    }(jQuery));


    (function ($, window, undefined) {
        var $document = $(document),
            supportTouch = $.mobile.support.touch,
            scrollEvent = "touchmove scroll",
            touchStartEvent = supportTouch ? "touchstart" : "mousedown",
            touchStopEvent = supportTouch ? "touchend" : "mouseup",
            touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

        // setup new event shortcuts
        $.each(( "touchstart touchmove touchend " +
        "tap taphold " +
        "swipe swipeleft swiperight " +
        "scrollstart scrollstop" ).split(" "), function (i, name) {

            $.fn[name] = function (fn) {
                return fn ? this.bind(name, fn) : this.trigger(name);
            };

            // jQuery < 1.8
            if ($.attrFn) {
                $.attrFn[name] = true;
            }
        });

        function triggerCustomEvent(obj, eventType, event, bubble) {
            var originalType = event.type;
            event.type = eventType;
            if (bubble) {
                $.event.trigger(event, undefined, obj);
            } else {
                $.event.dispatch.call(obj, event);
            }
            event.type = originalType;
        }

        // also handles scrollstop
        $.event.special.scrollstart = {

            enabled: true,
            setup: function () {

                var thisObject = this,
                    $this = $(thisObject),
                    scrolling,
                    timer;

                function trigger(event, state) {
                    scrolling = state;
                    triggerCustomEvent(thisObject, scrolling ? "scrollstart" : "scrollstop", event);
                }

                // iPhone triggers scroll after a small delay; use touchmove instead
                $this.bind(scrollEvent, function (event) {

                    if (!$.event.special.scrollstart.enabled) {
                        return;
                    }

                    if (!scrolling) {
                        trigger(event, true);
                    }

                    clearTimeout(timer);
                    timer = setTimeout(function () {
                        trigger(event, false);
                    }, 50);
                });
            },
            teardown: function () {
                $(this).unbind(scrollEvent);
            }
        };

        // also handles taphold
        $.event.special.tap = {
            tapholdThreshold: 750,
            emitTapOnTaphold: true,
            setup: function () {
                var thisObject = this,
                    $this = $(thisObject),
                    isTaphold = false;

                $this.bind("vmousedown", function (event) {
                    isTaphold = false;
                    if (event.which && event.which !== 1) {
                        return false;
                    }

                    var origTarget = event.target,
                        timer;

                    function clearTapTimer() {
                        clearTimeout(timer);
                    }

                    function clearTapHandlers() {
                        clearTapTimer();

                        $this.unbind("vclick", clickHandler)
                            .unbind("vmouseup", clearTapTimer);
                        $document.unbind("vmousecancel", clearTapHandlers);
                    }

                    function clickHandler(event) {
                        clearTapHandlers();

                        // ONLY trigger a 'tap' event if the start target is
                        // the same as the stop target.
                        if (!isTaphold && origTarget === event.target) {
                            triggerCustomEvent(thisObject, "tap", event);
                        } else if (isTaphold) {
                            event.preventDefault();
                        }
                    }

                    $this.bind("vmouseup", clearTapTimer)
                        .bind("vclick", clickHandler);
                    $document.bind("vmousecancel", clearTapHandlers);

                    timer = setTimeout(function () {
                        if (!$.event.special.tap.emitTapOnTaphold) {
                            isTaphold = true;
                        }
                        triggerCustomEvent(thisObject, "taphold", $.Event("taphold", {target: origTarget}));
                    }, $.event.special.tap.tapholdThreshold);
                });
            },
            teardown: function () {
                $(this).unbind("vmousedown").unbind("vclick").unbind("vmouseup");
                $document.unbind("vmousecancel");
            }
        };

        // Also handles swipeleft, swiperight
        $.event.special.swipe = {

            // More than this horizontal displacement, and we will suppress scrolling.
            scrollSupressionThreshold: 30,

            // More time than this, and it isn't a swipe.
            durationThreshold: 1000,

            // Swipe horizontal displacement must be more than this.
            horizontalDistanceThreshold: 30,

            // Swipe vertical displacement must be less than this.
            verticalDistanceThreshold: 30,

            getLocation: function (event) {
                var winPageX = window.pageXOffset,
                    winPageY = window.pageYOffset,
                    x = event.clientX,
                    y = event.clientY;

                if (event.pageY === 0 && Math.floor(y) > Math.floor(event.pageY) ||
                    event.pageX === 0 && Math.floor(x) > Math.floor(event.pageX)) {

                    // iOS4 clientX/clientY have the value that should have been
                    // in pageX/pageY. While pageX/page/ have the value 0
                    x = x - winPageX;
                    y = y - winPageY;
                } else if (y < ( event.pageY - winPageY) || x < ( event.pageX - winPageX )) {

                    // Some Android browsers have totally bogus values for clientX/Y
                    // when scrolling/zooming a page. Detectable since clientX/clientY
                    // should never be smaller than pageX/pageY minus page scroll
                    x = event.pageX - winPageX;
                    y = event.pageY - winPageY;
                }

                return {
                    x: x,
                    y: y
                };
            },

            start: function (event) {
                var data = event.originalEvent.touches ?
                        event.originalEvent.touches[0] : event,
                    location = $.event.special.swipe.getLocation(data);
                return {
                    time: ( new Date() ).getTime(),
                    coords: [location.x, location.y],
                    origin: $(event.target)
                };
            },

            stop: function (event) {
                var data = event.originalEvent.touches ?
                        event.originalEvent.touches[0] : event,
                    location = $.event.special.swipe.getLocation(data);
                return {
                    time: ( new Date() ).getTime(),
                    coords: [location.x, location.y]
                };
            },

            handleSwipe: function (start, stop, thisObject, origTarget) {
                if (stop.time - start.time < $.event.special.swipe.durationThreshold &&
                    Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.horizontalDistanceThreshold &&
                    Math.abs(start.coords[1] - stop.coords[1]) < $.event.special.swipe.verticalDistanceThreshold) {
                    var direction = start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight";

                    triggerCustomEvent(thisObject, "swipe", $.Event("swipe", {
                        target: origTarget,
                        swipestart: start,
                        swipestop: stop
                    }), true);
                    triggerCustomEvent(thisObject, direction, $.Event(direction, {
                        target: origTarget,
                        swipestart: start,
                        swipestop: stop
                    }), true);
                    return true;
                }
                return false;

            },

            // This serves as a flag to ensure that at most one swipe event event is
            // in work at any given time
            eventInProgress: false,

            setup: function () {
                var events,
                    thisObject = this,
                    $this = $(thisObject),
                    context = {};

                // Retrieve the events data for this element and add the swipe context
                events = $.data(this, "mobile-events");
                if (!events) {
                    events = {length: 0};
                    $.data(this, "mobile-events", events);
                }
                events.length++;
                events.swipe = context;

                context.start = function (event) {

                    // Bail if we're already working on a swipe event
                    if ($.event.special.swipe.eventInProgress) {
                        return;
                    }
                    $.event.special.swipe.eventInProgress = true;

                    var stop,
                        start = $.event.special.swipe.start(event),
                        origTarget = event.target,
                        emitted = false;

                    context.move = function (event) {
                        if (!start || event.isDefaultPrevented()) {
                            return;
                        }

                        stop = $.event.special.swipe.stop(event);
                        if (!emitted) {
                            emitted = $.event.special.swipe.handleSwipe(start, stop, thisObject, origTarget);
                            if (emitted) {

                                // Reset the context to make way for the next swipe event
                                $.event.special.swipe.eventInProgress = false;
                            }
                        }
                        // prevent scrolling
                        if (Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.scrollSupressionThreshold) {
                            event.preventDefault();
                        }
                    };

                    context.stop = function () {
                        emitted = true;

                        // Reset the context to make way for the next swipe event
                        $.event.special.swipe.eventInProgress = false;
                        $document.off(touchMoveEvent, context.move);
                        context.move = null;
                    };

                    $document.on(touchMoveEvent, context.move)
                        .one(touchStopEvent, context.stop);
                };
                $this.on(touchStartEvent, context.start);
            },

            teardown: function () {
                var events, context;

                events = $.data(this, "mobile-events");
                if (events) {
                    context = events.swipe;
                    delete events.swipe;
                    events.length--;
                    if (events.length === 0) {
                        $.removeData(this, "mobile-events");
                    }
                }

                if (context) {
                    if (context.start) {
                        $(this).off(touchStartEvent, context.start);
                    }
                    if (context.move) {
                        $document.off(touchMoveEvent, context.move);
                    }
                    if (context.stop) {
                        $document.off(touchStopEvent, context.stop);
                    }
                }
            }
        };
        $.each({
            scrollstop: "scrollstart",
            taphold: "tap",
            swipeleft: "swipe.left",
            swiperight: "swipe.right"
        }, function (event, sourceEvent) {

            $.event.special[event] = {
                setup: function () {
                    $(this).bind(sourceEvent, $.noop);
                },
                teardown: function () {
                    $(this).unbind(sourceEvent);
                }
            };
        });

    })(jQuery, this);


}));
